#ifndef _M_EXCHANGE_H_
#define _M_EXCHANGE_H_

//头文件尽量自己定义的在上面 库在下面  防止某些宏不受影响
#include"../mqcommon/mq_helper.hpp"
#include"../mqcommon/mq_logger.hpp"
#include"../mqcommon/mq_msg.pb.h"
#include<google/protobuf/map.h>
#include<iostream>
#include<unordered_map>
#include<mutex>
#include<memory>

namespace xiang11mq
{
//1.定义交换机类
struct Exchange
{
    using ptr = std::shared_ptr<Exchange>;
    Exchange(){}
    Exchange(const std::string &ename, ExchangeType etype, bool edurable, bool eauto_delete,
        const google::protobuf::Map<std::string, std::string> &eargs)
        :name(ename)
        ,type(etype)
        ,durable(edurable)
        ,auto_delete(eauto_delete)
        ,args(eargs)
    {}
    //1.交换机名称
    std::string name;
    //2.交换机类型
    ExchangeType type;
    //3.交换机持久化标志
    bool durable; 
    //4.是否自动删除标志
    bool auto_delete;
    //5.其他参数
    google::protobuf::Map<std::string, std::string> args;

    //args 存储键值对 在存储数据库的时候 组织一个格式字符串 key=val&key=val
    //解析str_args字符串 将内容存储到成员中
    void setArgs(const std::string &str_args)
    {
        //key=val&key=val
        std::vector<std::string> sub_args;
        StrHelper::split(str_args, "&", sub_args);
        for(auto &str: sub_args)
        {
            size_t pos = str.find("=");
            if(pos == std::string::npos)
            {
                ELOG("Exchange 解析str_args 出现错误");
            }
            std::string key = str.substr(0, pos);
            std::string val = str.substr(pos + 1);
            args.insert({key, val});
        }
    }
    //将args的内容序列化之后 返回一个字符串
    std::string getArgs()
    {
        std::string result;
        for(auto [x, y] : args)
        {
            result += x + "=" + y + "&";
        }
        return result;
    }
};
using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;

//2.定义交换机持久化管理类
class ExchangeMapper
{
public:
    ExchangeMapper(const std::string &dbfile)
        :_sql_helper(dbfile)
    {
        std::string path = FileHelper::parentDirectory(dbfile);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        createTable();
    }
    //创建交换机表
    void createTable()
    {
        #define EXCHANGE_CREATE_TABLE "create table if not exists exchange_table(    \
            name varchar(32) primary key,                                   \
            type int,                                                       \
            durable int,                                                    \
            auto_delete int,                                                \
            args varchar(128)                                               \
        );"
        bool ret = _sql_helper.execute(EXCHANGE_CREATE_TABLE, nullptr, nullptr);
        if(ret == false)
        {
            DLOG("创建交换机表失败");
            abort();                    //异常退出
        }
    }
    //删除交换机表
    void removeTable()
    {
        #define EXCHANGE_DROP_TABLE "drop table if exists exchange_table;"
        bool ret = _sql_helper.execute(EXCHANGE_DROP_TABLE, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除交换机表失败");
            abort(); // 异常退出
        }
    }
    //新增交换机数据
    bool insert(Exchange::ptr &exp)
    {
        #define EXCHANGE_INSERT_SQL "insert into exchange_table values('%s', %d, %d, %d, '%s');"
        char sql_str[255] = {0};
        sprintf(sql_str, EXCHANGE_INSERT_SQL, exp->name.c_str(), (int)exp->type, (int)exp->durable, (int)exp->auto_delete, exp->getArgs().c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("新增交换机表数据失败");
            return false;
        }
        return true;
    }
    //删除交换机数据
    void remove(const std::string &name)
    {
        #define EXCHANGE_DELETE_SQL "delete from exchange_table where name = '%s';"
        char sql_str[255] = {0};
        sprintf(sql_str, EXCHANGE_DELETE_SQL, name.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除交换机表数据失败");
        }
    }
    
    // //获取单个交换机数据
    // Exchange::ptr getOne(std::string &name)
    // {

    // }
    //获取全部交换机数据    返回umap 比 vector查找效率更快
    //重启的时调用
    ExchangeMap recovery()
    {
        ExchangeMap result;
        #define EXCHANGE_SELECT_SQL "select name, type, durable, auto_delete, args from exchange_table;" 
        bool ret = _sql_helper.execute(EXCHANGE_SELECT_SQL, selectCallback, &result);
        if(ret == 0)
        {
            ELOG("数据恢复失败");
            abort();
        }
        return result;
    }
private:
    // typedef int (*SqliteCallback)(void*, int, char**, char**)
    static int selectCallback(void *arg, int numcol, char **row, char **fileds)
    {
        ExchangeMap *result = (ExchangeMap*) arg;
        auto exp = std::make_shared<Exchange>();
        exp->name = row[0];
        exp->type = (xiang11mq::ExchangeType)std::stoi(row[1]);
        exp->durable = (bool)std::stoi(row[2]);
        exp->auto_delete = (bool)std::stoi(row[3]);
        if(row[4])
            exp->setArgs(row[4]);
        result->insert(std::make_pair(exp->name, exp));
        return 0;       //必须有
    }

private:
    SqliteHelper _sql_helper;
};


//3.定义交换机数据内存管理类
class ExchangeManager
{
public:
    using ptr = std::shared_ptr<ExchangeManager>;
    ExchangeManager(const std::string &dbfile)
        :_mapper(dbfile)
    {
        _exchanges = _mapper.recovery();
    }
    //声明交换机 并添加存储
    bool declareExchange(const std::string &name, ExchangeType type, bool durable, bool auto_delete,
        const google::protobuf::Map<std::string, std::string> &args)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        //查询交换机是否存在    由于exists也要加锁 不能加同一把锁 所以自己判断
        auto it = _exchanges.find(name);
        if(it != _exchanges.end())
        {
            //如果交换机存在 直接返回
            DLOG("要添加的交换机已存在");
            return true;
        }
        auto exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
        //判断是否需要持久化存储tongyibasuo
        if(durable == true)
        {
            bool ret = _mapper.insert(exp);
            if(ret == false) return false;
        }
        _exchanges.insert(make_pair(exp->name, exp));
        return true;
    }
    //删除交换机
    void deleteExchange(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if(it == _exchanges.end())
        {
            //如果交换机存在 直接返回
            DLOG("要删除的交换机不存在");
            return;
        }
        if(it->second->durable == true)
            _mapper.remove(name);
        _exchanges.erase(name);
    }
    //获取指定交换机
    Exchange::ptr selectExchange(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if(it == _exchanges.end())
        {
            //如果交换机存在 直接返回
            DLOG("要获取的交换机不存在");
            return Exchange::ptr();
        }
        return it->second;
    }
    //查询交换机是否存在
    bool exists(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _exchanges.find(name);
        if(it == _exchanges.end())
        {
            return false;
        }
        return true;
    }
    //返回交换机数量
    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _exchanges.size();
    }
    //删除所有交换机数据
    void clear()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _exchanges.clear();
    }
private:
    std::mutex _mutex;
    ExchangeMapper _mapper;
    ExchangeMap _exchanges;
};

}




#endif